﻿using System;
using System.Collections.Generic;
using System.Linq;
using YawetagDemo.Framework.Data;
using YawetagDemo.Framework.Impl.Data;
using YawetagDemo.Framework.Services;
using YawetagLib.System.Data;
using YawetagLib.System.Services;
using YawetagLib.System.Utilities;

namespace YawetagDemo.Framework.Impl.Services
{
    public class MusicService : BaseService, IMusicService
    {
        public MusicService(IServiceFactory serviceFactory, IDataManager dataManager)
            : base(serviceFactory, dataManager)
        {
        }

        internal Artist GetArtist(int artistID)
        {
            return DataManager.Get<ArtistRepository>().Get(artistID);
        }

        IArtist IMusicService.GetArtist(int artistID)
        {
            return GetArtist(artistID);
        }

        internal IList<Artist> GetArtists()
        {
            return DataManager.Get<ArtistRepository>().GetAll();
        }

        IList<IArtist> IMusicService.GetArtists()
        {
            return GetArtists().OfType<IArtist>().ToList();
        }

        internal Artist CreateArtist(string name, DateTime? dob)
        {
            Person person = ServiceFactory.Get<CoreService>().CreatePerson(name, dob);
            Artist artist = new Artist { Person = person };

            DataManager.Get<ArtistRepository>().Insert(artist);

            return artist;
        }

        IArtist IMusicService.CreateArtist(string name, DateTime? dob)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                IArtist artist = CreateArtist(name, dob);

                transaction.Commit();

                return artist;
            }
        }

        internal void UpdateArtist(Artist artist, string name, DateTime? dob)
        {
            ServiceFactory.Get<CoreService>().UpdatePerson(artist.Person, name, dob);
            DataManager.Get<ArtistRepository>().Update(artist);
        }

        IArtist IMusicService.UpdateArtist(int artistID, string name, DateTime? dob)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                Artist artist = GetArtist(artistID);
                UpdateArtist(artist, name, dob);

                transaction.Commit();

                return artist;
            }
        }

        internal void DeleteArtist(Artist artist)
        {
            ServiceFactory.Get<CoreService>().DeletePerson(artist.Person);
        }

        void IMusicService.DeleteArtist(int artistID)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                Artist artist = GetArtist(artistID);
                DeleteArtist(artist);

                transaction.Commit();
            }
        }

        internal Band GetBand(int bandID)
        {
            return DataManager.Get<BandRepository>().Get(bandID);
        }

        IBand IMusicService.GetBand(int bandID)
        {
            return GetBand(bandID);
        }

        internal IList<Band> GetBands()
        {
            return DataManager.Get<BandRepository>().GetAll();
        }

        IList<IBand> IMusicService.GetBands()
        {
            return GetBands().OfType<IBand>().ToList();
        }

        internal Band CreateBand(string name)
        {
            Band band = new Band { Name = name };
            DataManager.Get<BandRepository>().Insert(band);

            return band;
        }

        IBand IMusicService.CreateBand(string name)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                Band band = CreateBand(name);

                transaction.Commit();

                return band;
            }
        }

        internal void UpdateBand(Band band, string name)
        {
            band.Name = name;
            DataManager.Get<BandRepository>().Update(band);
        }

        IBand IMusicService.UpdateBand(int bandID, string name)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                Band band = GetBand(bandID);
                UpdateBand(band, name);

                transaction.Commit();

                return band;
            }
        }

        internal void DeleteBand(Band band)
        {
            DataManager.Get<BandRepository>().Delete(band);
        }

        void IMusicService.DeleteBand(int bandID)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                DeleteBand(GetBand(bandID));

                transaction.Commit();
            }
        }

        internal void AddArtistToBand(Band band, Artist artist)
        {
            DataManager.Get<BandArtistRepository>().Insert(new BandArtist { Artist = artist, Band = band });
        }

        void IMusicService.AddArtistToBand(int bandID, int artistID)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                AddArtistToBand(GetBand(bandID), GetArtist(artistID));

                transaction.Commit();
            }
        }

        internal void RemoveArtistFromBand(Band band, Artist artist)
        {
            BandArtistRepository bandArtistRepository = DataManager.Get<BandArtistRepository>();

            bandArtistRepository.Delete(bandArtistRepository.Get(band.BandID, artist.ArtistID));
        }

        void IMusicService.RemoveArtistFromBand(int bandID, int artistID)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                RemoveArtistFromBand(GetBand(bandID), GetArtist(artistID));

                transaction.Commit();
            }
        }

        internal Album GetAlbum(int albumID)
        {
            return DataManager.Get<AlbumRepository>().Get(albumID);
        }

        IAlbum IMusicService.GetAlbum(int albumID)
        {
            return DataManager.Get<AlbumRepository>().Get(albumID);
        }

        internal Album CreateAlbum(Band band, string name, DateTime releaseDate)
        {
            Album album = new Album { Band = band };
            UpdateAlbumFields(album, name, releaseDate);
            DataManager.Get<AlbumRepository>().Insert(album);

            return album;
        }

        IAlbum IMusicService.CreateAlbum(int bandID, string name, DateTime releaseDate)
        {
            Band band = DataManager.Get<BandRepository>().Get(bandID);

            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                Album album = CreateAlbum(band, name, releaseDate);

                transaction.Commit();

                return album;
            }
        }

        internal void UpdateAlbum(Album album, string name, DateTime releaseDate)
        {
            UpdateAlbumFields(album, name, releaseDate);
            DataManager.Get<AlbumRepository>().Update(album);
        }

        IAlbum IMusicService.UpdateAlbum(int albumID, string name, DateTime releaseDate)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                Album album = GetAlbum(albumID);
                UpdateAlbum(album, name, releaseDate);

                transaction.Commit();

                return album;
            }
        }

        private static void UpdateAlbumFields(Album album, string name, DateTime releaseDate)
        {
            album.Name = name;
            album.ReleaseDate = releaseDate;
        }

        internal void RankAlbum(Album album, Person person, byte stars)
        {
            AlbumRankingRepository albumRankingRepository = DataManager.Get<AlbumRankingRepository>();
            AlbumRanking albumRanking = albumRankingRepository.GetOrDefault(album.AlbumID, person.PersonID);

            if (albumRanking == null)
            {
                albumRanking = new AlbumRanking { Album = album, Person = person, Stars = stars };
                albumRankingRepository.Insert(albumRanking);
            }
            else
            {
                albumRanking.Stars = stars;
                albumRankingRepository.Update(albumRanking);
            }
        }

        void IMusicService.RankAlbum(int albumID, int personID, byte stars)
        {
            Album album = DataManager.Get<AlbumRepository>().Get(albumID);
            Person person = ServiceFactory.Get<CoreService>().GetPerson(personID);

            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                RankAlbum(album, person, stars);

                transaction.Commit();
            }
        }

        internal void DeleteAlbum(Album album)
        {
            DataManager.Get<AlbumRepository>().Delete(album);
        }

        void IMusicService.DeleteAlbum(int albumID)
        {
            using (ITransaction transaction = DataManager.BeginTransaction())
            {
                DeleteAlbum(GetAlbum(albumID));

                transaction.Commit();
            }
        }

        void IMusicService.LogArtists(Func<IArtist, string> getArtist)
        {
            Console.WriteLine("[...] Artist presentation list being gotten from the client side");
            IList<Artist> artists = GetArtists();

            foreach (Artist artist in artists)
            {
                Console.WriteLine(getArtist(artist));
            }

            Console.WriteLine();
        }

        PagedResultInfo<IAlbumRankingItem> IMusicService.GetAlbumRankingItems(string filterCriteria, string sortExpression, 
            int startRowIndex, int? maximumRows)
        {
            return DataManager.Get<AlbumRankingRepository>()
                .GetPagedResult(filterCriteria, sortExpression, startRowIndex, maximumRows)
                .OfType<AlbumRankingItem, IAlbumRankingItem>();
        }
    }
}
